package com.gitee.apanlh.util.algorithm.encrypt.asymmetric;

import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;

/**	
 * 	SM2非对称国密算法（该类实现了SM2的加密、解密、签名和验证功能）
 * 	<p>将默认采用C1C3C2算法，默认曲线sm2p256v1
 * 	<p>支持多种方式加载公钥和私钥，可以从原始的公钥Q值和私钥D值，或者从X509格式的公钥和私钥，或者从OpenSSH格式的公钥和私钥来加载
 * 	<p>注意: 公钥和私钥字符串仅限原有公钥或密钥经过Base64编码或十六进制编码
 * 	<p>本类提供公钥/私钥进行编码操作 {@link #getPublicEncodeToHex()}, {@link #getPrivateEncodeToHex()}等等
 *  #mark 获取根据Q值以及D值还原有问题
 *   
 * 	@author Pan
 */
public class SM2 extends SM2AsymmetricAbstract {
	
	/**
	 * 	默认构造函数
	 * 	<br>默认C1C3C2模式
	 * 	
	 * 	@author Pan
	 */
	public SM2() {
		this(SM2Mode.C1C3C2);
	}
	
	/**
	 * 	构造函数-自定义模式
	 * 	
	 * 	@author Pan
	 * 	@param 	sm2Mode	SM2模式
	 */
	public SM2(SM2Mode sm2Mode) {
		super(sm2Mode);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2
	 * 	<br>注意:字符串仅限原有公钥或密钥经过Base64编码或十六进制编码
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 */
	public SM2(String publicKey, String privateKey) {
		this(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>注意:字符串仅限原有公钥或密钥经过Base64编码或十六进制编码
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 */
	public SM2(String publicKey, String privateKey, SM2Mode sm2Mode) {
		super(publicKey, privateKey, sm2Mode);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<pre>支持格式:
	 * 	原始Q值,D值方式
	 * 	X509(PublicKey,PrivateKey)
	 * 	OpenSSH方式
	 * 	</pre>
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 */
	public SM2(byte[] publicKey, byte[] privateKey) {
		this(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<pre>支持格式:
	 * 	原始Q值,D值方式
	 * 	X509(PublicKey,PrivateKey)
	 * 	OpenSSH方式
	 * 	</pre>
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 */
	public SM2(byte[] publicKey, byte[] privateKey, SM2Mode sm2Mode) {
		super(publicKey, privateKey, sm2Mode);
	}
	
	/**
	 * 	构造函数-根据公钥Q值或私钥D值来进行还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	q	公钥Q值
	 * 	@param 	d	私钥D值
	 */
	public SM2(byte[] q, BigInteger d) {
		this(q, d, SM2Mode.C1C3C2);
	}
	
	/**
	 * 	构造函数-根据公钥Q值或私钥D值来进行还原
	 * <br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 * 	@param 	sm2Mode	SM2模式
	 */
	public SM2(byte[] q, BigInteger d, SM2Mode sm2Mode) {
		super(q, d.toByteArray(), sm2Mode);
	}
	
	/**
	 * 	构造函数-根据公钥Q值或私钥D值来进行还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 */
	public SM2(ECPoint q, BigInteger d) {
		this(q, d, SM2Mode.C1C3C2);
	}
	
	/**
	 * 	构造函数-根据公钥Q值或私钥D值来进行还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 * 	@param 	sm2Mode	SM2模式
	 */
	public SM2(ECPoint q, BigInteger d, SM2Mode sm2Mode) {
		super(q, d, sm2Mode);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<br>X509(PublicKey,PrivateKey)
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 */
	public SM2(PublicKey publicKey, PrivateKey privateKey) {
		this(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构造函数-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<br>X509(PublicKey,PrivateKey)
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 */
	public SM2(PublicKey publicKey, PrivateKey privateKey, SM2Mode sm2Mode) {
		super(publicKey, privateKey, sm2Mode);
	}
	
	/**	
	 * 	构造函数-加载公钥及私钥
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 */
	public SM2(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) {
		super(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构造函数-加载公钥及私钥
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 */
	public SM2(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey, SM2Mode sm2Mode) {
		super(publicKey, privateKey, sm2Mode);
	}
	
	/**
	 * 	构建-默认C1C3C2模式
	 * 	
	 * 	@author Pan
	 * 	@return	SM2
	 */
	public static SM2 create() {
		return new SM2();
	}
	
	/**
	 * 	构建-自定义模式
	 * 	
	 * 	@author Pan
	 * 	@param 	sm2Mode	SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(SM2Mode sm2Mode) {
		return new SM2(sm2Mode);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2
	 * 	<br>注意:字符串仅限原有公钥或密钥经过Base64编码或十六进制编码
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@return	SM2
	 */
	public static SM2 create(String publicKey, String privateKey) {
		return new SM2(publicKey, privateKey);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>注意:字符串仅限原有公钥或密钥经过Base64编码或十六进制编码
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(String publicKey, String privateKey, SM2Mode sm2Mode) {
		return new SM2(publicKey, privateKey, sm2Mode);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<pre>支持格式:
	 * 	原始Q值,D值方式
	 * 	X509(PublicKey,PrivateKey)
	 * 	OpenSSH方式
	 * 	</pre>
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@return	SM2
	 */
	public static SM2 create(byte[] publicKey, byte[] privateKey) {
		return new SM2(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<pre>支持格式:
	 * 	原始Q值,D值方式
	 * 	X509(PublicKey,PrivateKey)
	 * 	OpenSSH方式
	 * 	</pre>
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(byte[] publicKey, byte[] privateKey, SM2Mode sm2Mode) {
		return new SM2(publicKey, privateKey, sm2Mode);
	}
	
	/**
	 * 	构建-根据公钥Q值或私钥D值来进行还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	q	公钥Q值
	 * 	@param 	d	私钥D值
	 * 	@return	SM2
	 */
	public static SM2 create(byte[] q, BigInteger d) {
		return new SM2(q, d, SM2Mode.C1C3C2);
	}
	
	/**
	 * 	构建-根据公钥Q值或私钥D值来进行还原
	 * <br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 * 	@param 	sm2Mode	SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(byte[] q, BigInteger d, SM2Mode sm2Mode) {
		return new SM2(q, d.toByteArray(), sm2Mode);
	}
	
	/**
	 * 	构建-根据公钥Q值或私钥D值来进行还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 * 	@return	SM2
	 */
	public static SM2 create(ECPoint q, BigInteger d) {
		return new SM2(q, d, SM2Mode.C1C3C2);
	}
	
	/**
	 * 	构建-根据公钥Q值或私钥D值来进行还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	@author Pan
	 * 	@param 	q		公钥Q值
	 * 	@param 	d		私钥D值
	 * 	@param 	sm2Mode	SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(ECPoint q, BigInteger d, SM2Mode sm2Mode) {
		return new SM2(q, d, sm2Mode);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<br>X509(PublicKey,PrivateKey)
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@return	SM2
	 */
	public static SM2 create(PublicKey publicKey, PrivateKey privateKey) {
		return new SM2(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构建-将解析公钥及私钥并还原
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	<br>X509(PublicKey,PrivateKey)
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(PublicKey publicKey, PrivateKey privateKey, SM2Mode sm2Mode) {
		return new SM2(publicKey, privateKey, sm2Mode);
	}
	
	/**	
	 * 	构建-加载公钥及私钥
	 * 	<br>默认C1C3C2模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@return	SM2
	 */
	public static SM2 create(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) {
		return new SM2(publicKey, privateKey, SM2Mode.C1C3C2);
	}
	
	/**	
	 * 	构建-加载公钥及私钥
	 * 	<br>自定义模式
	 * 	<br>根据加密或解密传递对应值，任意传递一个即可
	 * 	
	 * 	@author Pan
	 * 	@param 	publicKey	公钥
	 * 	@param 	privateKey	私钥
	 * 	@param 	sm2Mode		SM2模式
	 * 	@return	SM2
	 */
	public static SM2 create(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey, SM2Mode sm2Mode) {
		return new SM2(publicKey, privateKey, sm2Mode);
	}
}
